home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / phile.c < prev    next >
C/C++ Source or Header  |  1996-05-15  |  31KB  |  1,241 lines

  1. /*
  2.  * Program:    File routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    25 August 1993
  13.  * Last Edited:    15 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. #include <stdio.h>
  37. #include <ctype.h>
  38. #include <errno.h>
  39. extern int errno;        /* just in case */
  40. #include <signal.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "phile.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* File routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER philedriver = {
  57.   "phile",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   phile_valid,            /* mailbox is valid for us */
  60.   phile_parameters,        /* manipulate parameters */
  61.   phile_find,            /* find mailboxes */
  62.   phile_find_bboards,        /* find bboards */
  63.   phile_find_all,        /* find all mailboxes */
  64.   phile_find_all_bboards,    /* find all bboards */
  65.   phile_subscribe,        /* subscribe to mailbox */
  66.   phile_unsubscribe,        /* unsubscribe from mailbox */
  67.   phile_subscribe_bboard,    /* subscribe to bboard */
  68.   phile_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   phile_create,            /* create mailbox */
  70.   phile_delete,            /* delete mailbox */
  71.   phile_rename,            /* rename mailbox */
  72.   phile_open,            /* open mailbox */
  73.   phile_close,            /* close mailbox */
  74.   phile_fetchfast,        /* fetch message "fast" attributes */
  75.   phile_fetchflags,        /* fetch message flags */
  76.   phile_fetchstructure,        /* fetch message envelopes */
  77.   phile_fetchheader,        /* fetch message header only */
  78.   phile_fetchtext,        /* fetch message body only */
  79.   phile_fetchbody,        /* fetch message body section */
  80.   phile_setflag,        /* set message flag */
  81.   phile_clearflag,        /* clear message flag */
  82.   phile_search,            /* search for message based on criteria */
  83.   phile_ping,            /* ping mailbox to see if still alive */
  84.   phile_check,            /* check for new messages */
  85.   phile_expunge,        /* expunge deleted messages */
  86.   phile_copy,            /* copy messages to another mailbox */
  87.   phile_move,            /* move messages to another mailbox */
  88.   phile_append,            /* append string message to mailbox */
  89.   phile_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM phileproto = {&philedriver};
  94.  
  95. /* File validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *phile_valid (name)
  101.     char *name;
  102. {
  103.   char tmp[MAILTMPLEN];
  104.   return phile_isvalid (name,tmp) ? &philedriver : NIL;
  105. }
  106.  
  107.  
  108. /* File test for valid mailbox
  109.  * Accepts: mailbox name
  110.  * Returns: T if valid, NIL otherwise
  111.  */
  112.  
  113. int phile_isvalid (name,tmp)
  114.     char *name;
  115.     char *tmp;
  116. {
  117.   struct stat sbuf;
  118.   char *s;
  119.                 /* INBOX never accepted, any other name is */
  120.   return ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  121.       (s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
  122.       !(sbuf.st_mode & S_IFDIR) && sbuf.st_size);
  123. }
  124.  
  125. /* File manipulate driver parameters
  126.  * Accepts: function code
  127.  *        function-dependent value
  128.  * Returns: function-dependent return value
  129.  */
  130.  
  131. void *phile_parameters (function,value)
  132.     long function;
  133.     void *value;
  134. {
  135.   return NIL;
  136. }
  137.  
  138. /* File find list of mailboxes
  139.  * Accepts: mail stream
  140.  *        pattern to search
  141.  */
  142.  
  143. void phile_find (stream,pat)
  144.     MAILSTREAM *stream;
  145.     char *pat;
  146. {
  147.   if (stream) dummy_find (NIL,pat);
  148. }
  149.  
  150.  
  151. /* File find list of bboards
  152.  * Accepts: mail stream
  153.  *        pattern to search
  154.  */
  155.  
  156. void phile_find_bboards (stream,pat)
  157.     MAILSTREAM *stream;
  158.     char *pat;
  159. {
  160.   if (stream) dummy_find_bboards (NIL,pat);
  161. }
  162.  
  163.  
  164. /* File find list of all mailboxes
  165.  * Accepts: mail stream
  166.  *        pattern to search
  167.  */
  168.  
  169. void phile_find_all (stream,pat)
  170.     MAILSTREAM *stream;
  171.     char *pat;
  172. {
  173.   if (stream) dummy_find_all (NIL,pat);
  174. }
  175.  
  176.  
  177. /* File find list of all bboards
  178.  * Accepts: mail stream
  179.  *        pattern to search
  180.  */
  181.  
  182. void phile_find_all_bboards (stream,pat)
  183.     MAILSTREAM *stream;
  184.     char *pat;
  185. {
  186.   if (stream) dummy_find_all_bboards (NIL,pat);
  187. }
  188.  
  189. /* File subscribe to mailbox
  190.  * Accepts: mail stream
  191.  *        mailbox to add to subscription list
  192.  * Returns: T on success, NIL on failure
  193.  */
  194.  
  195. long phile_subscribe (stream,mailbox)
  196.     MAILSTREAM *stream;
  197.     char *mailbox;
  198. {
  199.   char tmp[MAILTMPLEN];
  200.   return sm_subscribe (mailboxfile (tmp,mailbox));
  201. }
  202.  
  203.  
  204. /* File unsubscribe to mailbox
  205.  * Accepts: mail stream
  206.  *        mailbox to delete from subscription list
  207.  * Returns: T on success, NIL on failure
  208.  */
  209.  
  210. long phile_unsubscribe (stream,mailbox)
  211.     MAILSTREAM *stream;
  212.     char *mailbox;
  213. {
  214.   char tmp[MAILTMPLEN];
  215.   return sm_unsubscribe (mailboxfile (tmp,mailbox));
  216. }
  217.  
  218.  
  219. /* File subscribe to bboard
  220.  * Accepts: mail stream
  221.  *        bboard to add to subscription list
  222.  * Returns: T on success, NIL on failure
  223.  */
  224.  
  225. long phile_subscribe_bboard (stream,mailbox)
  226.     MAILSTREAM *stream;
  227.     char *mailbox;
  228. {
  229.   char tmp[MAILTMPLEN];
  230.   sprintf (tmp,"*%s",mailbox);
  231.   return sm_subscribe (tmp);
  232. }
  233.  
  234.  
  235. /* File unsubscribe to bboard
  236.  * Accepts: mail stream
  237.  *        bboard to delete from subscription list
  238.  * Returns: T on success, NIL on failure
  239.  */
  240.  
  241. long phile_unsubscribe_bboard (stream,mailbox)
  242.     MAILSTREAM *stream;
  243.     char *mailbox;
  244. {
  245.   char tmp[MAILTMPLEN];
  246.   sprintf (tmp,"*%s",mailbox);
  247.   return sm_subscribe (tmp);
  248. }
  249.  
  250. /* File create mailbox
  251.  * Accepts: MAIL stream
  252.  *        mailbox name to create
  253.  * Returns: T on success, NIL on failure
  254.  */
  255.  
  256. long phile_create (stream,mailbox)
  257.     MAILSTREAM *stream;
  258.     char *mailbox;
  259. {
  260.   return dummy_create (stream,mailbox);
  261. }
  262.  
  263.  
  264. /* File delete mailbox
  265.  * Accepts: MAIL stream
  266.  *        mailbox name to delete
  267.  * Returns: T on success, NIL on failure
  268.  */
  269.  
  270. long phile_delete (stream,mailbox)
  271.     MAILSTREAM *stream;
  272.     char *mailbox;
  273. {
  274.   return dummy_delete (stream,mailbox);
  275. }
  276.  
  277.  
  278. /* File rename mailbox
  279.  * Accepts: MAIL stream
  280.  *        old mailbox name
  281.  *        new mailbox name (or NIL for delete)
  282.  * Returns: T on success, NIL on failure
  283.  */
  284.  
  285. long phile_rename (stream,old,new)
  286.     MAILSTREAM *stream;
  287.     char *old;
  288.     char *new;
  289. {
  290.   return dummy_rename (stream,old,new);
  291. }
  292.  
  293. /* File open
  294.  * Accepts: Stream to open
  295.  * Returns: Stream on success, NIL on failure
  296.  */
  297.  
  298. MAILSTREAM *phile_open (stream)
  299.     MAILSTREAM *stream;
  300. {
  301.   int i,k,fd;
  302.   unsigned long j,m;
  303.   char *s,tmp[MAILTMPLEN];
  304.   struct passwd *pw;
  305.   struct stat sbuf;
  306.   struct tm *t;
  307.   MESSAGECACHE *elt;
  308.                 /* return prototype for OP_PROTOTYPE call */
  309.   if (!stream) return &phileproto;
  310.   if (LOCAL) {            /* close old file if stream being recycled */
  311.     phile_close (stream);    /* dump and save the changes */
  312.     stream->dtb = &philedriver;    /* reattach this driver */
  313.     mail_free_cache (stream);    /* clean up cache */
  314.   }
  315.                 /* canonicalize the stream mailbox name */
  316.   mailboxfile (tmp,stream->mailbox);
  317.   if (*stream->mailbox != '*') {/* canonicalize name */
  318.     fs_give ((void **) &stream->mailbox);
  319.     stream->mailbox = cpystr (tmp);
  320.   }
  321.                 /* open mailbox */
  322.   if (stat (tmp,&sbuf) || (fd = open (tmp,O_RDONLY,NIL)) < 0) {
  323.     sprintf (tmp,"Unable to open file %s",stream->mailbox);
  324.     mm_log (tmp,ERROR);
  325.     return NIL;
  326.   }
  327.   stream->local = fs_get (sizeof (PHILELOCAL));
  328.                 /* initialize file information */
  329.   (elt = mail_elt (stream,1))->rfc822_size = sbuf.st_size;
  330.   elt->valid = elt->recent = T;    /* mark valid flags */
  331.   stream->sequence++;        /* bump sequence number */
  332.                 /* only one message */
  333.   stream->nmsgs = stream->recent = 1;
  334.   stream->rdonly = T;        /* make sure upper level knows readonly */
  335.                 /* instantiate a new envelope and body */
  336.   LOCAL->env = mail_newenvelope ();
  337.   LOCAL->body = mail_newbody ();
  338.  
  339.   t = gmtime (&sbuf.st_mtime);    /* get UTC time and Julian day */
  340.   i = t->tm_hour * 60 + t->tm_min;
  341.   k = t->tm_yday;
  342.   t = localtime(&sbuf.st_mtime);/* get local time */
  343.                 /* calulate time delta */
  344.   i = t->tm_hour * 60 + t->tm_min - i;
  345.   if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
  346.   k = abs (i);            /* time from UTC either way */
  347.   elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
  348.   elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
  349.   elt->year = t->tm_year - (BASEYEAR - 1900);
  350.   elt->zoccident = (k == i) ? 0 : 1;
  351.   elt->zhours = k/60;
  352.   elt->zminutes = k % 60;
  353.   sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
  354.        days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  355.        t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
  356.        elt->zhours,elt->zminutes);
  357.                 /* set up Date field */
  358.   LOCAL->env->date = cpystr (tmp);
  359.  
  360.                 /* fill in From field from file owner */
  361.   LOCAL->env->from = mail_newaddr ();
  362.   if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
  363.   else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
  364.   LOCAL->env->from->mailbox = cpystr (tmp);
  365.   LOCAL->env->from->host = cpystr (mylocalhost ());
  366.                 /* set subject to be mailbox name */
  367.   LOCAL->env->subject = cpystr (stream->mailbox);
  368.                 /* slurp the data */
  369.   read (fd,LOCAL->buf = (char *) fs_get (elt->rfc822_size+1),elt->rfc822_size);
  370.   LOCAL->buf[elt->rfc822_size] = '\0';
  371.   close (fd);            /* close the file */
  372.                 /* analyze data type */
  373.   if (i = phile_type ((unsigned char *) LOCAL->buf,elt->rfc822_size,&j)) {
  374.     LOCAL->body->type = TYPETEXT;
  375.     LOCAL->body->subtype = cpystr ("PLAIN");
  376.     if (!(i & PTYPECRTEXT)) {    /* change Internet newline format as needed */
  377.       STRING bs;
  378.       INIT (&bs,mail_string,(void *) (s = LOCAL->buf),elt->rfc822_size);
  379.       LOCAL->buf = (char *) fs_get ((m = strcrlflen (&bs) + 1));
  380.       strcrlfcpy (&LOCAL->buf,&m,s,elt->rfc822_size);
  381.       elt->rfc822_size = m;    /* update size */
  382.       fs_give ((void **) &s);    /* flush original UNIX-format string */
  383.     }
  384.     LOCAL->body->parameter = mail_newbody_parameter ();
  385.     LOCAL->body->parameter->attribute = cpystr ("charset");
  386.     LOCAL->body->parameter->value =
  387.       cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
  388.           (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
  389.           (i & PTYPE8) ? "ISO-8859-1" : "US-ASCII");
  390.     LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
  391.     LOCAL->body->size.lines = j;
  392.   }
  393.   else {            /* binary data */
  394.     LOCAL->body->type = TYPEAPPLICATION;
  395.     LOCAL->body->subtype = cpystr ("OCTET-STREAM");
  396.     LOCAL->body->parameter = mail_newbody_parameter ();
  397.     LOCAL->body->parameter->attribute = cpystr ("name");
  398.     LOCAL->body->parameter->value =
  399.       cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
  400.     LOCAL->body->encoding = ENCBASE64;
  401.     LOCAL->buf = (char *) rfc822_binary (s = LOCAL->buf,elt->rfc822_size,
  402.                      &elt->rfc822_size);
  403.     fs_give ((void **) &s);    /* flush originary binary contents */
  404.   }
  405.   LOCAL->body->size.bytes = elt->rfc822_size;
  406.   elt->rfc822_size += strlen (phile_fetchheader (stream,1));
  407.   mail_exists (stream,1);    /* make sure upper level knows */
  408.   mail_recent (stream,1);
  409.   return stream;        /* return stream alive to caller */
  410. }
  411.  
  412. /* File determine data type
  413.  * Accepts: data to examine
  414.  *        size of data
  415.  *        pointer to line count return
  416.  * Returns: PTYPE mask of data type
  417.  */
  418.  
  419. int phile_type (s,i,j)
  420.     unsigned char *s;
  421.     unsigned long i;
  422.     unsigned long *j;
  423. {
  424.   int ret = PTYPETEXT;
  425.   char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  426.   *j = 0;            /* no lines */
  427.                 /* check type of every character */
  428.   while (i--) switch (charvec[*s++]) {
  429.   case 'A':
  430.     ret |= PTYPE8;        /* 8bit character */
  431.     break;
  432.   case 'a':
  433.     break;            /* ASCII character */
  434.   case 'b':
  435.     return PTYPEBINARY;        /* binary byte seen, stop immediately */
  436.   case 'c':
  437.     ret |= PTYPECRTEXT;        /* CR indicates Internet text */
  438.     break;
  439.   case 'e':            /* ESC */
  440.     if (*s == '$') {        /* ISO-2022 sequence? */
  441.       if (s[1] == 'B' || s[1] == '@') ret |= PTYPEISO2022JP;
  442.       else if (s[1] == ')' && s[2] == 'C') ret |= PTYPEISO2022JP;
  443.     }
  444.     break;
  445.   case 'l':            /* newline */
  446.     (*j)++;
  447.     break;
  448.   }
  449.   return ret;            /* return type of data */
  450. }
  451.  
  452. /* File close
  453.  * Accepts: MAIL stream
  454.  */
  455.  
  456. void phile_close (stream)
  457.     MAILSTREAM *stream;
  458. {
  459.   if (LOCAL) {            /* only if a file is open */
  460.                 /* free local texts */
  461.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  462.                 /* nuke the local data */
  463.     fs_give ((void **) &stream->local);
  464.     stream->dtb = NIL;        /* log out the DTB */
  465.   }
  466. }
  467.  
  468.  
  469. /* File fetch fast information
  470.  * Accepts: MAIL stream
  471.  *        sequence
  472.  */
  473.  
  474. void phile_fetchfast (stream,sequence)
  475.     MAILSTREAM *stream;
  476.     char *sequence;
  477. {
  478.   return;            /* no-op for local mail */
  479. }
  480.  
  481.  
  482. /* File fetch flags
  483.  * Accepts: MAIL stream
  484.  *        sequence
  485.  */
  486.  
  487. void phile_fetchflags (stream,sequence)
  488.     MAILSTREAM *stream;
  489.     char *sequence;
  490. {
  491.   return;            /* no-op for local mail */
  492. }
  493.  
  494. /* File fetch structure
  495.  * Accepts: MAIL stream
  496.  *        message # to fetch
  497.  *        pointer to return body
  498.  * Returns: envelope of this message, body returned in body value
  499.  *
  500.  * Fetches the "fast" information as well
  501.  */
  502.  
  503. ENVELOPE *phile_fetchstructure (stream,msgno,body)
  504.     MAILSTREAM *stream;
  505.     long msgno;
  506.     BODY **body;
  507. {
  508.   if (body) *body = LOCAL->body;
  509.   return LOCAL->env;        /* return the envelope */
  510. }
  511.  
  512.  
  513. /* File fetch message header
  514.  * Accepts: MAIL stream
  515.  *        message # to fetch
  516.  * Returns: message header in RFC822 format
  517.  */
  518.  
  519. char *phile_fetchheader (stream,msgno)
  520.     MAILSTREAM *stream;
  521.     long msgno;
  522. {
  523.   BODY *body;
  524.   ENVELOPE *env = phile_fetchstructure (stream,msgno,&body);
  525.   rfc822_header (LOCAL->tmp,env,body);
  526.   return LOCAL->tmp;
  527. }
  528.  
  529.  
  530. /* File fetch message text (only)
  531.     body only;
  532.  * Accepts: MAIL stream
  533.  *        message # to fetch
  534.  * Returns: message text in RFC822 format
  535.  */
  536.  
  537. char *phile_fetchtext (stream,msgno)
  538.     MAILSTREAM *stream;
  539.     long msgno;
  540. {
  541.                 /* mark message as seen */
  542.   mail_elt (stream,msgno)->seen = T;
  543.   return LOCAL->buf;
  544. }
  545.  
  546.  
  547. /* File fetch message body as a structure
  548.  * Accepts: Mail stream
  549.  *        message # to fetch
  550.  *        section specifier
  551.  *        pointer to length
  552.  * Returns: pointer to section of message body
  553.  */
  554.  
  555. char *phile_fetchbody (stream,m,s,len)
  556.     MAILSTREAM *stream;
  557.     long m;
  558.     char *s;
  559.     unsigned long *len;
  560. {
  561.   MESSAGECACHE *elt = mail_elt (stream,m);
  562.   if (!strcmp (s,"0")) {    /* BODY[0] case? */
  563.     char *s = phile_fetchheader (stream,1);
  564.     *len = strlen (s);
  565.     return s;
  566.   }
  567.   else if (strcmp (s,"1")) return NIL;
  568.   *len = LOCAL->body->size.bytes;
  569.   elt->seen = T;        /* mark message as seen */
  570.   return LOCAL->buf;
  571. }
  572.  
  573. /* File set flag
  574.  * Accepts: MAIL stream
  575.  *        sequence
  576.  *        flag(s)
  577.  */
  578.  
  579. void phile_setflag (stream,sequence,flag)
  580.     MAILSTREAM *stream;
  581.     char *sequence;
  582.     char *flag;
  583. {
  584.   MESSAGECACHE *elt;
  585.   long i;
  586.   short f = phile_getflags (stream,flag);
  587.   if (!f) return;        /* no-op if no flags to modify */
  588.                 /* get sequence and loop on it */
  589.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  590.     if ((elt = mail_elt (stream,i))->sequence) {
  591.                 /* set all requested flags */
  592.       if (f&fSEEN) elt->seen = T;
  593.       if (f&fDELETED) elt->deleted = T;
  594.       if (f&fFLAGGED) elt->flagged = T;
  595.       if (f&fANSWERED) elt->answered = T;
  596.     }
  597. }
  598.  
  599.  
  600. /* File clear flag
  601.  * Accepts: MAIL stream
  602.  *        sequence
  603.  *        flag(s)
  604.  */
  605.  
  606. void phile_clearflag (stream,sequence,flag)
  607.     MAILSTREAM *stream;
  608.     char *sequence;
  609.     char *flag;
  610. {
  611.   MESSAGECACHE *elt;
  612.   long i;
  613.   short f = phile_getflags (stream,flag);
  614.   if (!f) return;        /* no-op if no flags to modify */
  615.                 /* get sequence and loop on it */
  616.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  617.     if ((elt = mail_elt (stream,i))->sequence) {
  618.                 /* clear all requested flags */
  619.       if (f&fSEEN) elt->seen = NIL;
  620.       if (f&fDELETED) elt->deleted = NIL;
  621.       if (f&fFLAGGED) elt->flagged = NIL;
  622.       if (f&fANSWERED) elt->answered = NIL;
  623.     }
  624. }
  625.  
  626. /* File search for messages
  627.  * Accepts: MAIL stream
  628.  *        search criteria
  629.  */
  630.  
  631. void phile_search (stream,criteria)
  632.     MAILSTREAM *stream;
  633.     char *criteria;
  634. {
  635.   long i,n;
  636.   char *d,tmp[MAILTMPLEN];
  637.   search_t f;
  638.                 /* initially all searched */
  639.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  640.                 /* get first criterion */
  641.   if (criteria && (criteria = strtok (criteria," "))) {
  642.                 /* for each criterion */
  643.     for (; criteria; (criteria = strtok (NIL," "))) {
  644.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  645.       switch (*ucase (criteria)) {
  646.       case 'A':            /* possible ALL, ANSWERED */
  647.     if (!strcmp (criteria+1,"LL")) f = phile_search_all;
  648.     else if (!strcmp (criteria+1,"NSWERED")) f = phile_search_answered;
  649.     break;
  650.       case 'B':            /* possible BCC, BEFORE, BODY */
  651.     if (!strcmp (criteria+1,"CC"))
  652.       f = phile_search_string (phile_search_bcc,&d,&n);
  653.     else if (!strcmp (criteria+1,"EFORE"))
  654.       f = phile_search_date (phile_search_before,&n);
  655.     else if (!strcmp (criteria+1,"ODY"))
  656.       f = phile_search_string (phile_search_body,&d,&n);
  657.     break;
  658.       case 'C':            /* possible CC */
  659.     if (!strcmp (criteria+1,"C"))
  660.       f = phile_search_string (phile_search_cc,&d,&n);
  661.     break;
  662.       case 'D':            /* possible DELETED */
  663.     if (!strcmp (criteria+1,"ELETED")) f = phile_search_deleted;
  664.     break;
  665.       case 'F':            /* possible FLAGGED, FROM */
  666.     if (!strcmp (criteria+1,"LAGGED")) f = phile_search_flagged;
  667.     else if (!strcmp (criteria+1,"ROM"))
  668.       f = phile_search_string (phile_search_from,&d,&n);
  669.     break;
  670.       case 'K':            /* possible KEYWORD */
  671.     if (!strcmp (criteria+1,"EYWORD"))
  672.       f = phile_search_flag (phile_search_keyword,&d);
  673.     break;
  674.       case 'N':            /* possible NEW */
  675.     if (!strcmp (criteria+1,"EW")) f = phile_search_new;
  676.     break;
  677.  
  678.       case 'O':            /* possible OLD, ON */
  679.     if (!strcmp (criteria+1,"LD")) f = phile_search_old;
  680.     else if (!strcmp (criteria+1,"N"))
  681.       f = phile_search_date (phile_search_on,&n);
  682.     break;
  683.       case 'R':            /* possible RECENT */
  684.     if (!strcmp (criteria+1,"ECENT")) f = phile_search_recent;
  685.     break;
  686.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  687.     if (!strcmp (criteria+1,"EEN")) f = phile_search_seen;
  688.     else if (!strcmp (criteria+1,"INCE"))
  689.       f = phile_search_date (phile_search_since,&n);
  690.     else if (!strcmp (criteria+1,"UBJECT"))
  691.       f = phile_search_string (phile_search_subject,&d,&n);
  692.     break;
  693.       case 'T':            /* possible TEXT, TO */
  694.     if (!strcmp (criteria+1,"EXT"))
  695.       f = phile_search_string (phile_search_text,&d,&n);
  696.     else if (!strcmp (criteria+1,"O"))
  697.       f = phile_search_string (phile_search_to,&d,&n);
  698.     break;
  699.       case 'U':            /* possible UN* */
  700.     if (criteria[1] == 'N') {
  701.       if (!strcmp (criteria+2,"ANSWERED")) f = phile_search_unanswered;
  702.       else if (!strcmp (criteria+2,"DELETED")) f = phile_search_undeleted;
  703.       else if (!strcmp (criteria+2,"FLAGGED")) f = phile_search_unflagged;
  704.       else if (!strcmp (criteria+2,"KEYWORD"))
  705.         f = phile_search_flag (phile_search_unkeyword,&d);
  706.       else if (!strcmp (criteria+2,"SEEN")) f = phile_search_unseen;
  707.     }
  708.     break;
  709.       default:            /* we will barf below */
  710.     break;
  711.       }
  712.       if (!f) {            /* if can't determine any criteria */
  713.     sprintf (tmp,"Unknown search criterion: %.30s",criteria);
  714.     mm_log (tmp,ERROR);
  715.     return;
  716.       }
  717.                 /* run the search criterion */
  718.       for (i = 1; i <= stream->nmsgs; ++i)
  719.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  720.       mail_elt (stream,i)->searched = NIL;
  721.     }
  722.                 /* report search results to main program */
  723.     for (i = 1; i <= stream->nmsgs; ++i)
  724.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  725.   }
  726. }
  727.  
  728. /* File ping mailbox
  729.  * Accepts: MAIL stream
  730.  * Returns: T if stream alive, else NIL
  731.  * No-op for readonly files, since read/writer can expunge it from under us!
  732.  */
  733.  
  734. long phile_ping (stream)
  735.     MAILSTREAM *stream;
  736. {
  737.   return T;
  738. }
  739.  
  740. /* File check mailbox
  741.  * Accepts: MAIL stream
  742.  * No-op for readonly files, since read/writer can expunge it from under us!
  743.  */
  744.  
  745. void phile_check (stream)
  746.     MAILSTREAM *stream;
  747. {
  748.   mm_log ("Check completed",NIL);
  749. }
  750.  
  751. /* File expunge mailbox
  752.  * Accepts: MAIL stream
  753.  */
  754.  
  755. void phile_expunge (stream)
  756.     MAILSTREAM *stream;
  757. {
  758.   mm_log ("Expunge ignored on readonly mailbox",NIL);
  759. }
  760.  
  761. /* File copy message(s)
  762.     s;
  763.  * Accepts: MAIL stream
  764.  *        sequence
  765.  *        destination mailbox
  766.  * Returns: T if copy successful, else NIL
  767.  */
  768.  
  769. long phile_copy (stream,sequence,mailbox)
  770.     MAILSTREAM *stream;
  771.     char *sequence;
  772.     char *mailbox;
  773. {
  774.   mm_log ("Copy not valid for file",ERROR);
  775.   return NIL;
  776. }
  777.  
  778.  
  779. /* File move message(s)
  780.     s;
  781.  * Accepts: MAIL stream
  782.  *        sequence
  783.  *        destination mailbox
  784.  * Returns: T if move successful, else NIL
  785.  */
  786.  
  787. long phile_move (stream,sequence,mailbox)
  788.     MAILSTREAM *stream;
  789.     char *sequence;
  790.     char *mailbox;
  791. {
  792.   mm_log ("Move not valid for file",ERROR);
  793.   return NIL;
  794. }
  795.  
  796.  
  797. /* File append message from stringstruct
  798.  * Accepts: MAIL stream
  799.  *        destination mailbox
  800.  *        stringstruct of messages to append
  801.  * Returns: T if append successful, else NIL
  802.  */
  803.  
  804. long phile_append (stream,mailbox,flags,date,message)
  805.     MAILSTREAM *stream;
  806.     char *mailbox;
  807.     char *flags;
  808.     char *date;
  809.                 STRING *message;
  810. {
  811.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  812.   sprintf (tmp,"Can't append - file \"%s\" is not in valid mailbox format",
  813.        mailboxfile (file,mailbox));
  814.   mm_log (tmp,ERROR);
  815.   return NIL;
  816. }
  817.  
  818.  
  819. /* File garbage collect stream
  820.  * Accepts: Mail stream
  821.  *        garbage collection flags
  822.  */
  823.  
  824. void phile_gc (stream,gcflags)
  825.     MAILSTREAM *stream;
  826.     long gcflags;
  827. {
  828.   /* nothing here for now */
  829. }
  830.  
  831. /* Internal routines */
  832.  
  833.  
  834. /* Parse flag list
  835.  * Accepts: MAIL stream
  836.  *        flag list as a character string
  837.  * Returns: flag command list
  838.  */
  839.  
  840.  
  841. short phile_getflags (stream,flag)
  842.     MAILSTREAM *stream;
  843.     char *flag;
  844. {
  845.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  846.   short f = 0;
  847.   short i,j;
  848.   if (flag && *flag) {        /* no-op if no flag string */
  849.                 /* check if a list and make sure valid */
  850.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  851.       mm_log ("Bad flag list",ERROR);
  852.       return NIL;
  853.     }
  854.                 /* copy the flag string w/o list construct */
  855.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  856.     tmp[j] = '\0';
  857.     t = ucase (tmp);        /* uppercase only from now on */
  858.  
  859.     while (t && *t) {        /* parse the flags */
  860.       if (*t == '\\') {        /* system flag? */
  861.     switch (*++t) {        /* dispatch based on first character */
  862.     case 'S':        /* possible \Seen flag */
  863.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  864.       t += 4;        /* skip past flag name */
  865.       break;
  866.     case 'D':        /* possible \Deleted flag */
  867.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  868.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  869.       t += 7;        /* skip past flag name */
  870.       break;
  871.     case 'F':        /* possible \Flagged flag */
  872.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  873.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  874.       t += 7;        /* skip past flag name */
  875.       break;
  876.     case 'A':        /* possible \Answered flag */
  877.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  878.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  879.       t += 8;        /* skip past flag name */
  880.       break;
  881.     default:        /* unknown */
  882.       i = 0;
  883.       break;
  884.     }
  885.                 /* add flag to flags list */
  886.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  887.       }
  888.       else {            /* no user flags yet */
  889.     t = strtok (t," ");    /* isolate flag name */
  890.     sprintf (err,"Unknown flag: %.80s",t);
  891.     t = strtok (NIL," ");    /* get next flag */
  892.     mm_log (err,ERROR);
  893.       }
  894.     }
  895.   }
  896.   return f;
  897. }
  898.  
  899. /* Search support routines
  900.  * Accepts: MAIL stream
  901.  *        message number
  902.  *        pointer to additional data
  903.  * Returns: T if search matches, else NIL
  904.  */
  905.  
  906.  
  907. char phile_search_all (stream,msgno,d,n)
  908.     MAILSTREAM *stream;
  909.     long msgno;
  910.     char *d;
  911.     long n;
  912. {
  913.   return T;            /* ALL always succeeds */
  914. }
  915.  
  916.  
  917. char phile_search_answered (stream,msgno,d,n)
  918.     MAILSTREAM *stream;
  919.     long msgno;
  920.     char *d;
  921.     long n;
  922. {
  923.   return mail_elt (stream,msgno)->answered ? T : NIL;
  924. }
  925.  
  926.  
  927. char phile_search_deleted (stream,msgno,d,n)
  928.     MAILSTREAM *stream;
  929.     long msgno;
  930.     char *d;
  931.     long n;
  932. {
  933.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  934. }
  935.  
  936.  
  937. char phile_search_flagged (stream,msgno,d,n)
  938.     MAILSTREAM *stream;
  939.     long msgno;
  940.     char *d;
  941.     long n;
  942. {
  943.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  944. }
  945.  
  946.  
  947. char phile_search_keyword (stream,msgno,d,n)
  948.     MAILSTREAM *stream;
  949.     long msgno;
  950.     char *d;
  951.     long n;
  952. {
  953.   return NIL;            /* keywords not supported yet */
  954. }
  955.  
  956.  
  957. char phile_search_new (stream,msgno,d,n)
  958.     MAILSTREAM *stream;
  959.     long msgno;
  960.     char *d;
  961.     long n;
  962. {
  963.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  964.   return (elt->recent && !elt->seen) ? T : NIL;
  965. }
  966.  
  967. char phile_search_old (stream,msgno,d,n)
  968.     MAILSTREAM *stream;
  969.     long msgno;
  970.     char *d;
  971.     long n;
  972. {
  973.   return mail_elt (stream,msgno)->recent ? NIL : T;
  974. }
  975.  
  976.  
  977. char phile_search_recent (stream,msgno,d,n)
  978.     MAILSTREAM *stream;
  979.     long msgno;
  980.     char *d;
  981.     long n;
  982. {
  983.   return mail_elt (stream,msgno)->recent ? T : NIL;
  984. }
  985.  
  986.  
  987. char phile_search_seen (stream,msgno,d,n)
  988.     MAILSTREAM *stream;
  989.     long msgno;
  990.     char *d;
  991.     long n;
  992. {
  993.   return mail_elt (stream,msgno)->seen ? T : NIL;
  994. }
  995.  
  996.  
  997. char phile_search_unanswered (stream,msgno,d,n)
  998.     MAILSTREAM *stream;
  999.     long msgno;
  1000.     char *d;
  1001.     long n;
  1002. {
  1003.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1004. }
  1005.  
  1006.  
  1007. char phile_search_undeleted (stream,msgno,d,n)
  1008.     MAILSTREAM *stream;
  1009.     long msgno;
  1010.     char *d;
  1011.     long n;
  1012. {
  1013.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1014. }
  1015.  
  1016.  
  1017. char phile_search_unflagged (stream,msgno,d,n)
  1018.     MAILSTREAM *stream;
  1019.     long msgno;
  1020.     char *d;
  1021.     long n;
  1022. {
  1023.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1024. }
  1025.  
  1026.  
  1027. char phile_search_unkeyword (stream,msgno,d,n)
  1028.     MAILSTREAM *stream;
  1029.     long msgno;
  1030.     char *d;
  1031.     long n;
  1032. {
  1033.   return T;            /* keywords not supported yet */
  1034. }
  1035.  
  1036.  
  1037. char phile_search_unseen (stream,msgno,d,n)
  1038.     MAILSTREAM *stream;
  1039.     long msgno;
  1040.     char *d;
  1041.     long n;
  1042. {
  1043.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1044. }
  1045.  
  1046. char phile_search_before (stream,msgno,d,n)
  1047.     MAILSTREAM *stream;
  1048.     long msgno;
  1049.     char *d;
  1050.     long n;
  1051. {
  1052.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1053.   return (char) ((long) ((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1054. }
  1055.  
  1056.  
  1057. char phile_search_on (stream,msgno,d,n)
  1058.     MAILSTREAM *stream;
  1059.     long msgno;
  1060.     char *d;
  1061.     long n;
  1062. {
  1063.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1064.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1065. }
  1066.  
  1067.  
  1068. char phile_search_since (stream,msgno,d,n)
  1069.     MAILSTREAM *stream;
  1070.     long msgno;
  1071.     char *d;
  1072.     long n;
  1073. {
  1074.                 /* everybody interprets "since" as .GE. */
  1075.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1076.   return (char)((long) ((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1077. }
  1078.  
  1079.  
  1080. char phile_search_body (stream,msgno,d,n)
  1081.     MAILSTREAM *stream;
  1082.     long msgno;
  1083.     char *d;
  1084.     long n;
  1085. {
  1086.   return search (LOCAL->buf,mail_elt (stream,msgno)->rfc822_size,d,n);
  1087. }
  1088.  
  1089.  
  1090. char phile_search_subject (stream,msgno,d,n)
  1091.     MAILSTREAM *stream;
  1092.     long msgno;
  1093.     char *d;
  1094.     long n;
  1095. {
  1096.   char *s = phile_fetchstructure (stream,msgno,NIL)->subject;
  1097.   return s ? search (s,strlen (s),d,n) : NIL;
  1098. }
  1099.  
  1100.  
  1101. char phile_search_text (stream,msgno,d,n)
  1102.     MAILSTREAM *stream;
  1103.     long msgno;
  1104.     char *d;
  1105.     long n;
  1106. {
  1107.   return phile_search_body (stream,msgno,d,n);
  1108. }
  1109.  
  1110. char phile_search_bcc (stream,msgno,d,n)
  1111.     MAILSTREAM *stream;
  1112.     long msgno;
  1113.     char *d;
  1114.     long n;
  1115. {
  1116.   char tmp[MAILTMPLEN];
  1117.   tmp[0] = '\0';        /* initially empty string */
  1118.                 /* get text for address */
  1119.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->bcc);
  1120.   return search (tmp,strlen (tmp),d,n);
  1121. }
  1122.  
  1123.  
  1124. char phile_search_cc (stream,msgno,d,n)
  1125.     MAILSTREAM *stream;
  1126.     long msgno;
  1127.     char *d;
  1128.     long n;
  1129. {
  1130.   char tmp[MAILTMPLEN];
  1131.   tmp[0] = '\0';        /* initially empty string */
  1132.                 /* get text for address */
  1133.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->cc);
  1134.   return search (tmp,strlen (tmp),d,n);
  1135. }
  1136.  
  1137.  
  1138. char phile_search_from (stream,m,d,n)
  1139.     MAILSTREAM *stream;
  1140.     long m;
  1141.     char *d;
  1142.     long n;
  1143. {
  1144.   char tmp[MAILTMPLEN];
  1145.   tmp[0] = '\0';        /* initially empty string */
  1146.                 /* get text for address */
  1147.   rfc822_write_address (tmp,phile_fetchstructure (stream,m,NIL)->from);
  1148.   return search (tmp,strlen (tmp),d,n);
  1149. }
  1150.  
  1151.  
  1152. char phile_search_to (stream,msgno,d,n)
  1153.     MAILSTREAM *stream;
  1154.     long msgno;
  1155.     char *d;
  1156.     long n;
  1157. {
  1158.   char tmp[MAILTMPLEN];
  1159.   tmp[0] = '\0';        /* initially empty string */
  1160.                 /* get text for address */
  1161.   rfc822_write_address (tmp,phile_fetchstructure (stream,msgno,NIL)->to);
  1162.   return search (tmp,strlen (tmp),d,n);
  1163. }
  1164.  
  1165. /* Search parsers */
  1166.  
  1167.  
  1168. /* Parse a date
  1169.  * Accepts: function to return
  1170.  *        pointer to date integer to return
  1171.  * Returns: function to return
  1172.  */
  1173.  
  1174. search_t phile_search_date (f,n)
  1175.     search_t f;
  1176.     long *n;
  1177. {
  1178.   long i;
  1179.   char *s;
  1180.   MESSAGECACHE elt;
  1181.                 /* parse the date and return fn if OK */
  1182.   return (phile_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1183.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1184. }
  1185.  
  1186. /* Parse a flag
  1187.  * Accepts: function to return
  1188.  *        pointer to string to return
  1189.  * Returns: function to return
  1190.  */
  1191.  
  1192. search_t phile_search_flag (f,d)
  1193.     search_t f;
  1194.     char **d;
  1195. {
  1196.                 /* get a keyword, return if OK */
  1197.   return (*d = strtok (NIL," ")) ? f : NIL;
  1198. }
  1199.  
  1200.  
  1201. /* Parse a string
  1202.  * Accepts: function to return
  1203.  *        pointer to string to return
  1204.  *        pointer to string length to return
  1205.  * Returns: function to return
  1206.  */
  1207.  
  1208.  
  1209. search_t phile_search_string (f,d,n)
  1210.     search_t f;
  1211.     char **d;
  1212.     long *n;
  1213. {
  1214.   char *end = " ";
  1215.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1216.   if (!c) return NIL;        /* missing argument */
  1217.   switch (*c) {            /* see what the argument is */
  1218.   case '{':            /* literal string */
  1219.     *n = strtol (c+1,d,10);    /* get its length */
  1220.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1221.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1222.       char e = *--c;
  1223.       *c = DELIM;        /* make sure not a space */
  1224.       strtok (c," ");        /* reset the strtok mechanism */
  1225.       *c = e;            /* put character back */
  1226.       break;
  1227.     }
  1228.   case '\0':            /* catch bogons */
  1229.   case ' ':
  1230.     return NIL;
  1231.   case '"':            /* quoted string */
  1232.     if (strchr (c+1,'"')) end = "\"";
  1233.     else return NIL;
  1234.   default:            /* atomic string */
  1235.     if (*d = strtok (c,end)) *n = strlen (*d);
  1236.     else return NIL;
  1237.     break;
  1238.   }
  1239.   return f;
  1240. }
  1241.